/*
 * Based on Android Animation implementation.
 */

package se.lnu.axez.animation;


public class Animation<E> {
	public static final int REPEAT_COUNT_INFINITE = -1;
	public static final int REPEAT_MODE_RESTART = 1;
	public static final int REPEAT_MODE_REVERSE = 2;
	private static final long DEFAULT_DURATION = 1000L;
	
	private Interpolator interpolator = LinearInterpolator.newInstance();
	private long duration = DEFAULT_DURATION;
	private int repeatCount = 0;
	private int repeatMode = REPEAT_MODE_RESTART;
	private long startTime = -1;
	private boolean more = true;
    private boolean cycleFlip = false;
    private boolean ended = false;
    private boolean stop = false;
    private int repeated = 0;

	public Animation() {
		this.reset();
	}

	public final Interpolator getInterpolator() {
		return this.interpolator;
	}

	public final void setInterpolator(Interpolator interpolator) {
		if (interpolator == null) {
			throw new IllegalArgumentException("interpolator cannot be null");
		}
		
		this.interpolator = interpolator;
	}

	public final long getDuration() {
		return this.duration;
	}

	public final void setDuration(long duration) {
        if (duration < 0) {
            throw new IllegalArgumentException("duration must be positive");
        }

        this.duration = duration;
	}

	public final int getRepeatCount() {
		return repeatCount;
	}

	public final void setRepeatCount(int repeatCount) {
        if (repeatCount < 0) {
            repeatCount = REPEAT_COUNT_INFINITE;
        }

        this.repeatCount = repeatCount;
	}

	public final int getRepeatMode() {
		return repeatMode;
	}

	public final void setRepeatMode(int repeatMode) {
		this.repeatMode = repeatMode;
	}
	
	public final boolean ended() {
		return this.ended;
	}
	
	public final void reset() {
		this.startTime = -1;
		this.ended = false;
		this.repeated = 0;
		this.cycleFlip = false;
		this.stop = false;
	}
	
	public final void end() {
		this.startTime = 0L;
	}
	
    public final boolean animate(long currentTime, E targetObject) {
    	if (this.stop) {
    		return false;
    	}
    	
        if (this.startTime == -1L) {
        	this.startTime = currentTime;
        }

        float normalizedTime;
        if (this.duration != 0L) {
            normalizedTime = ((float) (currentTime - this.startTime)) / (float) duration;
        } else {
            normalizedTime = currentTime < startTime ? 0.0f : 1.0f;
        }

        final boolean expired = normalizedTime >= 1.0f;
        this.more = !expired;

        normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

        if (this.cycleFlip) {
            normalizedTime = 1.0f - normalizedTime;
        }

        final float interpolatedTime = this.interpolator.getInterpolation(normalizedTime);
        this.applyTransformation(interpolatedTime, targetObject);

        if (expired) {
            if (this.repeatCount == this.repeated) {
                if (!ended) {
                    ended = true;
                }
            } else {
                if (this.repeatCount > 0) {
                	this.repeated++;
                }

                if (this.repeatMode == REPEAT_MODE_REVERSE) {
                    cycleFlip = !cycleFlip;
                }

                this.startTime = -1;
                this.more = true;
            }
        }

        return more;
    }

    protected void applyTransformation(float interpolatedTime, E targetObject) {
    }
}
